<script setup lang="ts">
import { ref, h, nextTick, computed, watch } from 'vue';
import { VueFlow, useVueFlow} from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { ControlButton, Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'

import DropzoneBackground from './flowComponents/DnDBackground.vue'
import DnDSliderBar from './flowComponents/DnDSliderBar.vue'
import useDragAndDrop from './ts/useDnD'

import Dialog from './Dialog.vue'
import useDialog from './ts/useDialog';
import Icon from './icons/Icon.vue'
import HttpNode from './flowComponents/HttpNode.vue';
import AgentNode from './flowComponents/AgentNode.vue';
import AgentIfNode from './flowComponents/AgentIfNode.vue';
import ToolExecuteNode from './flowComponents/ToolExecuteNode.vue'
import ToolNode from './flowComponents/ToolNode.vue';
import HttpInNode from './flowComponents/HttpInNode.vue';
import HttpOutNode from './flowComponents/HttpOutNode.vue';
import HttpAudioInNode from './flowComponents/HttpAudioInNode.vue';
import HttpImageInNode from './flowComponents/HttpImageInNode.vue';
import STTNode from './flowComponents/STTNode.vue';
import MessageHistoryNode from './flowComponents/MessageHistoryNode.vue';
import MessageConcatNode from './flowComponents/MessageConcatNode.vue';
import ManualStrNode from './flowComponents/ManualStrNode.vue';
import ClearNode from './flowComponents/ClearNode.vue';
import AgentListTaskNode from './flowComponents/AgentListTaskNode.vue';
import ProjectProcessNode from './flowComponents/ProjectProcessNode.vue';
import { onMounted, onUnmounted } from 'vue'
import {useProjectsStore} from '@/stores/projectsStore';
const { updateProjectContent, projects } = useProjectsStore();
const { onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop()
const projectStore=useProjectsStore();
const props =withDefaults( defineProps<{
  id: string;
  style?: Record<string, any>; // 添加 style 属性
  iscontrolable?: boolean;
}>(),{
  iscontrolable:true,
})

/**
 * `useVueFlow` provides:
 * 1. a set of methods to interact with the VueFlow instance (like `fitView`, `setViewport`, `addEdges`, etc)
 * 2. a set of event-hooks to listen to VueFlow events (like `onInit`, `onNodeDragStop`, `onConnect`, etc)
 * 3. the internal state of the VueFlow instance (like `nodes`, `edges`, `viewport`, etc)
 */
const { onInit, onNodeDragStop, addEdges, setViewport, toObject, getEdges,
  onNodesChange, onEdgesChange, applyNodeChanges, applyEdgeChanges, onNodeDrag,
  findNode,addNodes, updateNode,removeSelectedElements,setNodes, setEdges,setElements,removeSelectedNodes,removeNodes,removeEdges,setCenter,
  getViewport,fitView,getSelectedNodes,getSelectedEdges,getConnectedEdges,addSelectedEdges,removeSelectedEdges

} = useVueFlow()

const dialog = useDialog()

const nodes =props.iscontrolable?ref(projects.find(p => p.id === props.id)?.content?.initialNodes??[]):ref(projectStore.allprojects.find(p => p.projectID === props.id)?.content?.initialNodes??[])

const edges =props.iscontrolable?ref(projects.find(p => p.id === props.id)?.content?.initialEdges??[]):ref(projectStore.allprojects.find(p => p.projectID === props.id)?.content?.initialEdges??[])
// onMounted(()=>{
//   if(!props.iscontrolable){
//     nodes.value=projectStore.allprojects.find(p => p.projectID === props.id)?.content?.initialNodes??[]
//     edges.value=projectStore.allprojects.find(p => p.projectID === props.id)?.content?.initialEdges??[]
//   }
// })
// our dark mode toggle flag
const dark = ref(false)
watch(()=>getSelectedNodes.value,(newVal,oldVal)=>{

  console.log("Selected Nodes:", newVal)
  if(newVal.length>oldVal.length){
    let tp:any[]=[]
    newVal.forEach((no:any) => {
      const ee = getConnectedEdges(no.id)
      tp=tp.concat(ee)
    })
    tp.forEach(ed =>{
      //自定义逻辑
      if(ed.style&&ed.style.stroke=='red'){
        ed.style = { stroke: '#ccc', strokeWidth: 2 }
      }else{
        ed.style = { stroke: 'red', strokeWidth: 3 }
      }
      
      // if(ed.style&&ed.style.stroke=='#ccc'){
      //   let cc='red'
      //   if(ed.sourceNode.selected){
      //     cc=ed.sourceNode.style.backgroundColor
      //   }else{
      //     cc=ed.targetNode.style.backgroundColor
      //   }
      //   ed.style = { stroke: cc, strokeWidth: 4 }
      // }
    } )
  }else{
    getEdges.value.forEach(ed=>{
      if(!ed.sourceNode.selected && !ed.targetNode.selected){
          ed.style = { stroke: '#ccc', strokeWidth: 2 }
      }
    })
  }
})
watch(() => getSelectedEdges.value, (newVal, oldVal) => {
  getEdges.value.forEach(ed => {
    if (ed.selected) {
      ed.style = { stroke: 'red', strokeWidth: 3 }
    }else{
      ed.style = { stroke: '#ccc', strokeWidth: 2 }
    }
  })
})
onMounted(() => {
  removeSelectedEdges(getSelectedEdges.value)
  removeSelectedNodes(getSelectedNodes.value)
  getEdges.value.forEach(ed => ed.style = { stroke: '#ccc', strokeWidth: 2 })
})
/**
const 
/**
 * This is a Vue Flow event-hook which can be listened to from anywhere you call the composable, instead of only on the main component
 * Any event that is available as `@event-name` on the VueFlow component is also available as `onEventName` on the composable and vice versa
 *
 * onInit is called when the VueFlow viewport is initialized
 */
onInit((vueFlowInstance) => {
  // instance is the same as the return of `useVueFlow`
  vueFlowInstance.fitView()
})
onNodeDrag(({event, nodes, node})=>{
  if(!props.iscontrolable) return
  if(node.extent!=null && Array.isArray(node.extent) && node.extent.find((extent:any)=>extent.parent!=undefined)){

    console.log('drag Prevent')
    event.preventDefault()
  }
})
/**
 * onNodeDragStop is called when a node is done being dragged
 *
 * Node drag events provide you with:
 * 1. the event object
 * 2. the nodes array (if multiple nodes are dragged)
 * 3. the node that initiated the drag
 * 4. any intersections with other nodes
 */

onNodeDragStop(({ event, nodes, node }) => {
  if(!props.iscontrolable) return
  updateNode(node.id, {...node})
  console.log('Node Drag Stop', { event, nodes, node })
})


/**
 * toObject transforms your current graph data to an easily persist-able object
 */
function logToObject() {
  console.log(toObject())
}

/**
 * Resets the current viewport transformation (zoom & pan)
 */
function resetTransform() {
  setViewport({ x: 0, y: 0, zoom: 1 })
}

function toggleDarkMode() {
  dark.value = !dark.value
}


// confirm check dialog
import {dialogMsg} from './ts/useDialog'
onNodesChange(async (changes) => {
  if(!props.iscontrolable) return
  const nextChanges = []
  
  for (const change of changes) {
    if (change.type === 'remove') {
      console.log("change:",change)
      const isConfirmed = await dialog.confirm(dialogMsg(change.id))
      if (isConfirmed) {
        nextChanges.push(change)
      }
    } else {
      nextChanges.push(change)
    }
  }
  applyNodeChanges(nextChanges)
  //console.log('Node Changes', toObject().nodes)
  updateProjectContent(props.id,{initialEdges:toObject().edges, initialNodes:toObject().nodes })
})
onEdgesChange(async (changes) => {
  if(!props.iscontrolable) return
  const nextChanges = []

  for (const change of changes) {
    if (change.type === 'remove') {
      const isConfirmed = await dialog.confirm(dialogMsg(change.id,"[From] "+change.sourceHandle+" [To] "+change.targetHandle))

      if (isConfirmed) {
        nextChanges.push(change)
      }
    } else {
      nextChanges.push(change)
    }
  }

  applyEdgeChanges(nextChanges)
  updateProjectContent(props.id, {initialEdges:toObject().edges, initialNodes:toObject().nodes },true)
})


import {useDDrag} from './ts/useDDrag';
const { panOnDrag, onDKeyDown, onDKeyUp } = useDDrag()
onMounted(() => {
  window.addEventListener('keydown', onDKeyDown);
  window.addEventListener('keyup', onDKeyUp);
});
onUnmounted(() => {
  window.removeEventListener('keydown', onDKeyDown);
  window.removeEventListener('keyup', onDKeyUp);
});
interface ConnectStartParams {
  nodeId: string | null;
  handleId: string | null;
  handleType: 'source' | 'target'
}
// Connection Valid
import useConnectionValid from './flowComponents/ts/connectionValid';
const {setDraggingLineSourceHandleId, unsetDraggingLineSourceHandleId} = useConnectionValid()
import  type { OnConnectStartParams } from '@vue-flow/core'
function onConnectStart({nodeId, handleId, handleType}: OnConnectStartParams) {
  if(!props.iscontrolable) return
  if(nodeId==null || handleId==null || handleType==null) return;
  setDraggingLineSourceHandleId(nodeId, handleId, handleType)
}
function onConnectEnd() {
  if(!props.iscontrolable) return
  unsetDraggingLineSourceHandleId()
}
function onConnect(params:any) {
  if(!props.iscontrolable) return
   addEdges(params)
}

// Copy Selected
import useCopySelected from './ts/useCopySelected';

const {copySelected, getPasteNodesAndEdges} = useCopySelected()


const handleKeyDown = (event: KeyboardEvent) => {
  if(!props.iscontrolable) return
  // 如果当前焦点在输入框或可编辑区域，跳过
  const target = event.target as HTMLElement;
  if (
    target.tagName === 'INPUT' ||
    target.tagName === 'TEXTAREA' ||
    target.isContentEditable
  ) {
    return;
  }

  let ni=getSelectedNodes.value
  let ei=getSelectedEdges.value
  if (event.ctrlKey && event.key === 'c') {
    copySelected(ni,ei);
  }
  if (event.ctrlKey && event.key === 'v') {
    const { nodes, edges } = getPasteNodesAndEdges()
    addNodes(nodes)
    addEdges(edges)
    // 粘贴后默认选中新节点和边
    removeSelectedNodes(ni)
    removeSelectedEdges(ei)
  }

};

onMounted(() => {
  document.addEventListener('keydown', handleKeyDown);
});


onUnmounted(() => {
  document.removeEventListener('keydown', handleKeyDown);
});

const sideWidth = ref(200)
let startx=0
let startW=200
const mousedown = (e: MouseEvent) => {
  startx=e.clientX
  startW=sideWidth.value
  document.addEventListener('mousemove', mousemove)
  document.addEventListener('mouseup', mouseup)
  e.preventDefault();
}
const mousemove = (e: MouseEvent) => {
  const offsetx=startx-e.clientX
  sideWidth.value=Math.min(800, Math.max(180, startW + offsetx)); 
  //console.log('startx', startx, 'e.clientX', e.clientX, 'offsetx', offsetx,'startW',startW,'sideWidth',sideWidth,'width:',sideBarRef.value?.clientWidth)
}
const mouseup = () => {
  document.removeEventListener('mousemove', mousemove)
  document.removeEventListener('mouseup', mouseup)
}
let dragBarRef=ref<HTMLDivElement>()
let sideBarRef=ref<HTMLDivElement>()

const nodeClick=(mnode :any)=>{
  let node=mnode.node
  let z=getViewport().zoom
  const { x, y } = node.computedPosition
  setCenter(x + node.dimensions.width / 2, y + node.dimensions.height / 2, {
    duration: 500 ,
    zoom:z
  })
  //console.log('nodeClick', node)
}
const isControlVisible=ref(true)
const isMinMapVisible=ref(true)
const isAddable=ref(true)
const setControlVisible=(visible?:boolean)=>{
  isControlVisible.value=visible??!isControlVisible.value
}
const setMinMapVisible=(visible?:boolean)=>{
  isMinMapVisible.value=visible??!isMinMapVisible.value
}
const setAddable=(visible?:boolean)=>{
  isAddable.value=visible??!isAddable.value
}
defineExpose({
  removeNodes,
  removeEdges,
  setNodes,
  setEdges,
  setElements,
  findNode,
  getSelectedNodes,
  getSelectedEdges,
  fitView,
  setControlVisible,
  setMinMapVisible,
  setAddable,
  isAddable
})
</script>

<template>
  <div @drop="onDrop" class="basic-flow">

    <VueFlow :nodes="nodes" :edges="edges" :class="{ dark}" :default-viewport="{ zoom: 0.5 }" class="basic-flow"
      style="flex:1" :min-zoom="0.0001" :max-zoom="4" :pan-on-drag="true" :apply-default="false" @connect="onConnect"
      @connect-start="onConnectStart" @connect-end="onConnectEnd" @dragover="onDragOver" @dragleave="onDragLeave">
      <DropzoneBackground :style="{
        backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
        transition: 'background-color 0.2s ease',
      }">
        <p v-if="isDragOver">Drop here</p>
      </DropzoneBackground>
      <template #node-http-in="props">
        <HttpInNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-http-out="props">
        <HttpOutNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-agent="props">
        <AgentNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-tool-execute="props">
        <ToolExecuteNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-tool="props">
        <ToolNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>

      <template #node-agent-list-task="props">
        <AgentListTaskNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-project-process="props">
        <ProjectProcessNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>

      <template #node-http-audio-in="props">
        <HttpAudioInNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-http-image-in="props">
        <HttpImageInNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-stt="props">
        <STTNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>

      <template #node-message-history="props">
        <MessageHistoryNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-message-concat="props">
        <MessageConcatNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-manual-str="props">
        <ManualStrNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-clear="props">
        <ClearNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>

      <template #node-http="props">
        <HttpNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>
      <template #node-agent-if="props">
        <AgentIfNode :id="props.id" :data="props.data" :toObject="toObject" />
      </template>

      <MiniMap @nodeClick="nodeClick" pannable v-show="isMinMapVisible" />

      <Controls position="top-left" v-show="isControlVisible">
        <ControlButton title="Reset Transform" @click="resetTransform">
          <Icon name="reset" />
        </ControlButton>

        <ControlButton title="Toggle Dark Mode" @click="toggleDarkMode">
          <Icon v-if="dark" name="sun" />
          <Icon v-else name="moon" />
        </ControlButton>

        <ControlButton title="Log `toObject`" @click="logToObject">
          <Icon name="log" />
        </ControlButton>
      </Controls>

      <Dialog />

    </VueFlow>

    <div class="drag-bar " @mousedown.stop="mousedown" ref="dragBarRef" v-if="isAddable"></div>
    <div :style="{ width: sideWidth + 'px' }" style="overflow-y:auto;" v-if="iscontrolable && isAddable">
      <DnDSliderBar ref="sideBarRef" />
    </div>

  </div>
</template>


<style >

@import 'https://gcore.jsdelivr.net/npm/@vue-flow/core@1.42.1/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/core@1.42.1/dist/theme-default.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/controls@latest/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/minimap@latest/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/node-resizer@latest/dist/style.css';

.drag-bar {
  width: 6px;
  height: 100%;
  cursor: ew-resize;
  background: rgba(0, 0, 0, 0.1);
}
.drag-bar:hover {
  background: rgba(0, 0, 0, 0.2);
}
.basic-flow {
    flex-direction:column;
    display:flex;
    height:100%
}

.vue-flow__minimap {
  transform: scale(75%);
  transform-origin: bottom right;
}

.basic-flow.dark {
    background:#2d3748;
    color:#fffffb
}

.basic-flow.dark .vue-flow__node {
    background:#4a5568;
    color:#fffffb
}

.basic-flow.dark .vue-flow__node.selected {
    background:#333;
    box-shadow:0 0 0 2px #2563eb
}

.basic-flow .vue-flow__controls {
    display:flex;
    flex-wrap:wrap;
    justify-content:center
}

.basic-flow.dark .vue-flow__controls {
    border:1px solid #FFFFFB
}

.basic-flow .vue-flow__controls .vue-flow__controls-button {
    border:none;
    border-right:1px solid #eee
}

.basic-flow .vue-flow__controls .vue-flow__controls-button svg {
    height:100%;
    width:100%
}

.basic-flow.dark .vue-flow__controls .vue-flow__controls-button {
    background:#333;
    fill:#fffffb;
    border:none
}

.basic-flow.dark .vue-flow__controls .vue-flow__controls-button:hover {
    background:#4d4d4d
}

.basic-flow.dark .vue-flow__edge-textbg {
    fill:#292524
}

.basic-flow.dark .vue-flow__edge-text {
    fill:#fffffb
}




.basic-flow aside {
    color:#000000;
    font-weight:700;
    border-right:1px solid #eee;
    cursor: pointer;
    padding:15px 10px;
    font-size:12px;
    background:#eeeeee;
}

.basic-flow aside .nodes>* {
    margin-bottom:10px;
    cursor:grab;
    font-weight:500;
    border-radius:8px;
    padding:8px 16px;
    border:1px solid #919191;
}

.basic-flow aside .description {
    margin-bottom:10px
}

.basic-flow .vue-flow-wrapper {
    flex-grow:1;
    height:100%
}

@media screen and (min-width: 640px) {
    .basic-flow {
    flex-direction:row
}

.basic-flow aside {
    min-width:25%
}


}

@media screen and (max-width: 639px) {
    .basic-flow aside .nodes {
    display:flex;
    flex-direction:row;
    gap:5px
}


}

.dropzone-background {
    position:relative;
    height:100%;
    width:100%
}

.dropzone-background .overlay {
    position:absolute;
    top:0;
    left:0;
    height:100%;
    width:100%;
    display:flex;
    align-items:center;
    justify-content:center;
    z-index:1;
    pointer-events:none
}















.vue-flow__handle {
    height:24px;
    width:10px;
    background:#aaa;
    border-radius:4px
}

.vue-flow__edges path {
    stroke-width:3
}

.vue-flow__node textarea{
  width: 100%;
}
.vue-flow__node.selected {
    box-shadow:0 0 0 2px #3877ff
}
.vue-flow__node {
  display: block;
    background-color:#f3f4f6;
    padding: 5px;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http.selected {
    box-shadow:0 0 0 2px #7aff38
}
.vue-flow__node-http input:focus {
    outline:none;
    box-shadow:0 0 0 2px #81ff47;
    transition:box-shadow .2s
}
.vue-flow__node-tool {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-tool.selected {
    box-shadow:0 0 0 2px #3877ff
}
.vue-flow__node-tool input:focus {
    outline:none;
    box-shadow:0 0 0 2px #3877ff;
    transition:box-shadow .2s
}
.vue-flow__node-tool-execute {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-tool-execute.selected {
    box-shadow:0 0 0 2px #3877ff
}
.vue-flow__node-tool-execute input:focus {
    outline:none;
    box-shadow:0 0 0 2px #1f418d;
    transition:box-shadow .2s
}

.vue-flow__node-agent {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-agent.selected {
    box-shadow:0 0 0 2px #48a3f2
}
.vue-flow__node-agent input:focus {
    outline:none;
    box-shadow:0 0 0 2px #48a3f2;
    transition:box-shadow .2s
}
.vue-flow__node-agent-if {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-agent-if.selected {
    box-shadow:0 0 0 2px #48a3f2
}
.vue-flow__node-agent-if input:focus {
    outline:none;
    box-shadow:0 0 0 2px #48a3f2;
    transition:box-shadow .2s
}

.vue-flow__node-stt {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http-out {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-http-in {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http-audio-in {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http-image-in {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}



.vue-flow__node-operator {
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:8px;
    padding:16px 24px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-operator.selected {
    box-shadow:0 0 0 2px #2563eb
}

.vue-flow__node-operator .buttons {
    display:flex;
    gap:8px
}

.vue-flow__node-operator button {
    border:none;
    cursor:pointer;
    background-color:#4a5568;
    border-radius:8px;
    color:#fff;
    box-shadow:0 0 10px #0000004d;
    width:40px;
    height:40px;
    font-size:16px;
    display:flex;
    align-items:center;
    justify-content:center
}

.vue-flow__node-operator button svg {
    width:100%;
    height:100%
}

.vue-flow__node-operator button:hover {
    background-color:#2563eb;
    transition:background-color .2s
}

.vue-flow__node-operator button.selected {
    background-color:#2563eb
}
.vue-flow__node-operator .vue-flow__handle[data-handleid=target-a] {
    top:25%
}

.vue-flow__node-operator .vue-flow__handle[data-handleid=target-b] {
    top:75%
}

.vue-flow__node-operator .vue-flow__handle {
    background-color:#2563eb
}

.vue-flow__node-result {
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:8px;
    padding:16px 24px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-result.selected {
    box-shadow:0 0 0 2px #5ec697
}

.vue-flow__node-result .result {
    display:flex;
    gap:8px;
    font-size:24px
}

input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {
    -webkit-appearance:none;
    margin:0
}

input[type=number] {
    -moz-appearance:textfield
}
/* NodeData容器样式 */
.NodeData {
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding: 10px;
  margin-left: 40px;
  margin-right: 40px;
}

/* 输入框行样式 */
.input-row,
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 文本输入框样式 */
.text-input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 100%;
  box-sizing: border-box;
}

/* 复选框样式 */
.checkbox {
  margin-top: 5px;
}
.top-0{
  top: 10%;
}
.top-1{
  top: 25%;
}
.top-2{
  top: 40%;
}
.top-3{
  top: 55%;
}
.top-4{
  top: 70%;
}
.top-5{
  top: 85%;
}
.HandleLeftSpan {
  margin-left: 10px;
  white-space: nowrap; /* 防止内容换行 */
  overflow: hidden;    /* 隐藏溢出的内容 */
  text-overflow: ellipsis; /* 显示省略号 */
  max-width: 50px;    /* 设置最大宽度 */
  display: inline-block;
}

.HandleRightSpan {
  text-align: right;
  display: inline-block;
  white-space: nowrap; /* 防止内容换行 */
  overflow: hidden;    /* 隐藏溢出的内容 */
  text-overflow: ellipsis; /* 显示省略号 */
  max-width: 50px;    /* 设置最大宽度 */
  position: relative;
  right: 50px;
}


.NodeName {
  position: relative;
  display: inline-block;
  font-size: 14px;
  width: 100%;
  padding-left: 70px;
  padding-right: 70px;
  text-align: center;
}

.NodeName input {
  max-height: 20px;
  height: 100%;
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  color: #333;
  font-size: 16px;
  padding: 8px 12px;
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
  text-align: center;
  background-color: #00000000;
}

/* 输入框聚焦时 */
.NodeName input:focus {
  background-color: #f5f5f5;
  box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.5);
  color: #000;
}

/* 输入框没有内容时（placeholder显示） */
.NodeName input:placeholder-shown {
  color: #ffffff;
}

/* 输入框内容区域边距 */
.NodeName input::placeholder {
  color: #ccc;
  font-style: italic;
}

/* 输入框平滑过渡效果 */
.NodeName input {
  transition: background-color 0.3s, box-shadow 0.3s, color 0.3s;
}

.NodeContent{
  height: 100%;
  width: 100%;
}

.input-row,
.select-row,
.checkbox-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 0;
}

.text-input,
.select-input {
  width: 120px;
  padding: 4px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.checkbox {
  margin-left: 8px;
}
.AgentFlowInputHandle{
  background: #2563eb;
}
.AgentFlowOutputHandle{
  background: #2563eb;
}
.AgentMsg{
  background: rgb(255, 110, 70);
}
.AgentMsgs{
  background: rgb(167, 84, 0);
}
.AgentAudio{
  background: rgb(194, 133, 0);
}
.AgentTools{
  background: rgb(147, 161, 0);
}
.AgentTool{
  background: rgb(196, 214, 0);
}
.AgentToolCalls{
  background: rgb(0, 156, 73);
}
.AgentToolCall{
  background: rgb(0, 255, 119);
}
.AgentImage{
  background: rgb(0, 255, 255);
}
</style>